/*****************************************************************
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations
 * under the License.
 ****************************************************************/
package reco.db.field;

import java.util.Collection;

import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;

import reco.db.Constraint;
import reco.db.ConstraintFactory;
import reco.db.Domain;

/**
 * Values which make sense to be compared with oth elements in a lists (as numbers o dates).
 * 
 * @param <T>
 *            field's type.
 */
@SuppressWarnings("serial")
public abstract class ValueField<T, D extends Domain<D>> extends SimpleValueField<T, D> {

    /**
     * Creates a field defining only its type, and leaving its definition open.
     * <p>
     * After the field construction is done, the client <em>must</em> define the field's name and domain, and <em>can</em> add constraints to the
     * field.
     * 
     * @param fieldType
     *            Type of field's possible values.
     * @param fieldConstraintsDefinitions
     *            Definitions of {@link Constraint}'s to be atacched to the created field during construction.
     * @throws NullPointerException
     *             if <code>fieldType</code> is <code>null</code>.
     * 
     * @see #setName(String)
     * @see #setDomain(Class)
     */
    public ValueField(Class<T> fieldType, ConstraintFactory... fieldConstraintsDefinitions) {
        super(fieldType, fieldConstraintsDefinitions);
    }

    /**
     * Creates a field defining its type, name and domain, and leaving its definition open.
     * <p>
     * After the field construction is done, the client <em>can</em> define the field's name and domain, and <em>can</em> add constraints to the
     * field.
     * 
     * @param fieldType
     *            Type of field's possible values.
     * @param fieldName
     *            Name used to recognize the field in the domain.
     * @param fieldDomain
     *            Domain where the field is defined.
     * @param fieldConstraintsDefinitions
     *            Definitions of {@link Constraint}'s to be atacched to the created field during construction.
     * @throws NullPointerException
     *             if <code>fieldType</code> is <code>null</code>.
     * @throws NullPointerException
     *             if <code>fieldName</code> is <code>null</code>.
     * @throws NullPointerException
     *             if <code>fieldDomain</code> is <code>null</code>.
     */
    public ValueField(Class<T> fieldType, String fieldName, Class<D> fieldDomain, ConstraintFactory... fieldConstraintsDefinitions) {
        super(fieldType, fieldName, fieldDomain, fieldConstraintsDefinitions);
    }

    /**
     * Creates a filter selecting only domains which has this field's value in a "possible values" list.
     * 
     * @param values
     *            array of values that make a domain "considered" by the created filter.
     * @return a not null filter only accepting domains with this field having one of the defined values.
     */
    public Expression in(T... values) {
        return ExpressionFactory.inExp(getName(), values);
    }

    /**
     * Creates a filter selecting only domains which has this field's value not in a "possible values" list.
     * 
     * @param values
     *            array of values that make a domain "not considered" by the created filter.
     * @return a not null filter only accepting domains with this field not having one of the defined values.
     */
    public Expression notIn(T... values) {
        return ExpressionFactory.notInExp(getName(), values);
    }

    /**
     * Creates a filter selecting only domains which has this field's value in a "possible values" list.
     * 
     * @param values
     *            collection of values that make a domain "considered" by the created filter.
     * @return a not null filter only accepting domains with this field having one of the defined values.
     */
    public Expression in(Collection<T> values) {
        return ExpressionFactory.inExp(getName(), values);
    }

    /**
     * Creates a filter selecting only domains which has this field's value not in a "possible values" list.
     * 
     * @param values
     *            collection of values that make a domain "not considered" by the created filter.
     * @return a not null filter only accepting domains with this field not having one of the defined values.
     */
    public Expression notIn(Collection<T> values) {
        return ExpressionFactory.notInExp(getName(), values);
    }

}